﻿//-------------------------------------------------------------------
//	Copyright (c) 2010-2014  Zhirnov Andrey
//	This file is part of the "UX Standard Lib" project.
//	See copyright notice in "UX_STL_Ext.h".
//-------------------------------------------------------------------

#include "openal.h"

#ifdef _UXS_USE_OPENAL

#include "../OS/OS.h"
#include "shell/ALSoundSystem.h"

namespace UX_STL
{
namespace UXTypesExt
{
	using namespace ALShell;

/*
=================================================
	очистить стэк ошибок OpenAL
=================================================
*/
	void TAPIErrorChecker::ClearALErrors()
	{
		for (uint i = 0; i < _GetMaxErrors() and alGetError() != AL_NO_ERROR; ++i)  {}
	}

/*
=================================================
	очистить стэк ошибок контекста OpenAL
=================================================
*/
	void TAPIErrorChecker::ClearALCErrors()
	{
		ALCdevice *	p_device = SOUNDSYS()->GetDevice();

		for (uint i = 0; i < _GetMaxErrors() and alcGetError( p_device ) != ALC_NO_ERROR; ++i)  {}
	}

/*
=================================================
	проверить не было ли ошибки OpenAL
=================================================
*/
	bool TAPIErrorChecker::CheckALError(uni_c_string pALFunc, uni_c_string pFunc, uni_c_string pFile, uint uLine)
	{
		ALenum	e_err = alGetError();

		if ( e_err == AL_NO_ERROR )
			return true;

		string	str("AL error: ");

		str << alGetString(e_err) << ", in " << pALFunc;
		str << ", function: " << pFunc;

		LOG_EXT( str.cstr(), e_log_msg::ERROR_2, pFile.cstr(), uLine );
		return false;
	}
	
/*
=================================================
	проверить не было ли ошибки в контексте OpenAL
=================================================
*/
	bool TAPIErrorChecker::CheckALCError(uni_c_string pALCFunc, uni_c_string pFunc, uni_c_string pFile, uint uLine)
	{
		ALCdevice *	p_device = SOUNDSYS()->GetDevice();
		ALenum		e_err	 = alcGetError( p_device );
		
		if ( e_err == ALC_NO_ERROR )
			return true;

		string	str("ALC error: ");

		str << alcGetString( p_device, e_err ) << ", in " << pALCFunc;
		str << ", function: " << pFunc;

		LOG_EXT( str.cstr(), e_log_msg::ERROR_2, pFile.cstr(), uLine );
		return false;
	}
	
/*
=================================================
	проверить не было ли ошибок OpenAL
=================================================
*/
	bool TAPIErrorChecker::CheckALErrors(uni_c_string pFile, uint uLine)
	{
		ALenum	e_err;
		bool	ret   = true;

		for (uint i = 0; i < _GetMaxErrors() and (e_err = alGetError()) != AL_NO_ERROR; ++i)
		{
			string	str("AL error: ");
			str << alGetString(e_err);

			LOG_EXT( str.cstr(), e_log_msg::ERROR_2, pFile.cstr(), uLine );
			ret = false;
		}

		return ret;
	}
	
/*
=================================================
	проверить не было ли ошибок в контексте OpenAL
=================================================
*/
	bool TAPIErrorChecker::CheckALCErrors(uni_c_string pFile, uint uLine)
	{
		ALCdevice *	p_device = SOUNDSYS()->GetDevice();
		ALenum		e_err;
		bool		ret   = true;

		for (uint i = 0; i < _GetMaxErrors() and (e_err = alcGetError( p_device )) != ALC_NO_ERROR; ++i)
		{
			string	str("ALC error: ");
			str << alcGetString( p_device, e_err );

			LOG_EXT( str.cstr(), e_log_msg::ERROR_2, pFile.cstr(), uLine );
			ret = false;
		}
		return ret;
	}

}	// UXTypesExt
}	// UX_STL



	

static	UX_STL::UXTypesExt::array< UX_STL::UXTypesExt::string >	_s_aALLog;	
static	UX_STL::UXTypesExt::Library							_s_sALLib(AL_LIBRARY_NAME);
static	UX_STL::UXTypesExt::atomic_int32						_s_iALRefCounter;


extern "C"
{

static const char * AL_GetErrorString()
{
	static bool	bNeedDeleteLastString = false;

	if ( !_s_aALLog.Empty() )
	{
		if ( bNeedDeleteLastString )
			_s_aALLog.PopBack();

		if ( _s_aALLog.Empty() )
			return null;

		bNeedDeleteLastString = true;

		return _s_aALLog.Back().cstr();
	}
	else
		return null;
}

#define __ADD_TO_LOG( _text )			_s_aALLog.PushFront( _text )

#define AL_GETPROCADDRESS( _address )	_s_sALLib.GetProc( _address )


/* Init OpenAL Library */
#ifdef PLATFORM_WINDOWS
#	define OPENAL_INIT_LIB( _ret )		_ret = _s_sALLib.IsValid()
#endif

#ifndef OPENAL_INIT_LIB
#	error not defined library initialization!
#endif


/* get OpenAL function address */
#if UX_AL_DEBUG_LOG == 0
#	define OPENAL_GET_PROC( p, n, d ) \
		if ( (n = (p)AL_GETPROCADDRESS( #n )) == null ) \
		{ \
			n = d; \
			ret &= false; \
		}
#else
#	define OPENAL_GET_PROC( p, n, d ) \
		if ( (n = (p)AL_GETPROCADDRESS( #n )) == null ) \
		{ \
			n = d; \
			__ADD_TO_LOG( "Can't get address of function \""#n"\"." ); \
			ret &= false; \
		}
#endif	// UX_AL_DEBUG_LOG




/* Create Default OpenAL functions */
#ifdef UX_AL_USE_DEFAULT_FUNC

	#undef  AL_BUILDFUNC
	#define AL_BUILDFUNC( _retType, _funcName, _funcParams, _retValue ) \
		AL_API _retType AL_API_CALL alDefault##_funcName _funcParams \
		{ \
			WARNING( #_funcName ); \
			return _retValue; \
		} \

	AL_FUNCTIONS
	AL_FUNCTIONS_EXT
	ALC_FUNCTIONS_EXT

	#undef  AL_BUILDFUNC
	#define AL_BUILDFUNC( _retType, _funcName, _funcParams, _retValue )

#endif	// UX_AL_USE_DEFAULT_FUNC



/* AL Functions */
#undef  AL_BUILDFUNC
#define AL_BUILDFUNC( _retType, _funcName, _funcParams, _retValue ) \
	PFN##_funcName		_funcName	= null;

AL_FUNCTIONS
AL_FUNCTIONS_EXT
ALC_FUNCTIONS_EXT

#undef  AL_BUILDFUNC
#define AL_BUILDFUNC( _retType, _funcName, _funcParams, _retValue )



AL_API const char * AL_GetError()
{
	return AL_GetErrorString();
}

AL_API void AL_ClearErrors()
{
	_s_aALLog.Clear();
}
	
AL_API bool AL_Delete()
{
	if ( --_s_iALRefCounter == 0 )
		return _s_sALLib.Unload();
	else
		return true;
}

AL_API char AL_Init()
{
	static bool		s_bInited = false;
	
	++_s_iALRefCounter;

	if ( s_bInited )
		return 0;	// success

	bool	ret = true;

	OPENAL_INIT_LIB( ret );

	if ( !ret ) {
		__ADD_TO_LOG( "Error: can't find OpenAL library." );
		return 2;	// fail
	}


	// version 1.0 //

	#undef  AL_BUILDFUNC
	#ifdef UX_AL_USE_DEFAULT_FUNC
		#define AL_BUILDFUNC( _retType, _funcName, _funcParams, _retValue ) \
			OPENAL_GET_PROC( PFN##_funcName,	_funcName, &alDefault##_funcName );
	#else
		#define AL_BUILDFUNC( _retType, _funcName, _funcParams, _retValue ) \
			OPENAL_GET_PROC( PFN##_funcName,	_funcName, null );
	#endif	// UX_AL_USE_DEFAULT_FUNC

	AL_FUNCTIONS

	#undef  AL_BUILDFUNC
	#define AL_BUILDFUNC( _retType, _funcName, _funcParams, _retValue )


	s_bInited = true;
	return ret ? 0 : 1;		// success or with errors
}


AL_API bool AL_InitExtensions(void *pALCDevice)
{
	bool		ret		= true;
	ALCdevice *	device	= (ALCdevice *)pALCDevice;

	// version 1.1 //
		
	#undef  AL_BUILDFUNC
	#ifdef UX_AL_USE_DEFAULT_FUNC
		#define AL_BUILDFUNC( _retType, _funcName, _funcParams, _retValue ) \
			OPENAL_GET_PROC( PFN##_funcName,	_funcName, &alDefault##_funcName );
	#else
		#define AL_BUILDFUNC( _retType, _funcName, _funcParams, _retValue ) \
			OPENAL_GET_PROC( PFN##_funcName,	_funcName, null );
	#endif	// UX_AL_USE_DEFAULT_FUNC
	

	#undef  AL_GETPROCADDRESS
	#define AL_GETPROCADDRESS( _address )	alGetProcAddress( _address )

	AL_FUNCTIONS_EXT

	#undef  AL_GETPROCADDRESS
	#define AL_GETPROCADDRESS( _address )	alcGetProcAddress( device, _address )

	ALC_FUNCTIONS_EXT

	#undef  AL_BUILDFUNC
	#define AL_BUILDFUNC( _retType, _funcName, _funcParams, _retValue )

	return ret;
}


#undef	__ADD_TO_LOG
#undef	AL_GETPROCADDRESS
#undef	OPENAL_INIT_LIB
#undef	OPENAL_GET_PROC


}	// extern "C"

#endif	// _UXS_USE_OPENAL
